package com.vcc.boss.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckSafe;
import com.mybatisflex.core.paginate.Page;
import com.vcc.boss.core.queue.transaction.queue.RiskQueue;
import com.vcc.core.annotations.RepeatSubmit;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.exception.ProgramException;
import com.vcc.data.entity.CardType;
import com.vcc.data.entity.Risk;
import com.vcc.data.entity.RiskRule;
import com.vcc.data.entity.User;
import com.vcc.service.boss.request.risk.*;
import com.vcc.service.boss.response.risk.RiskVoBoss;
import com.vcc.service.sa.BossUserUtil;
import com.vcc.service.service.CardTypeService;
import com.vcc.service.service.RiskRuleService;
import com.vcc.service.service.RiskService;
import com.vcc.service.service.UserService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("risk")
public class RiskController {


    @Resource
    private RiskService riskService;
    @Resource
    private UserService userService;
    @Resource
    private CardTypeService cardTypeService;
    @Resource
    private RiskRuleService riskRuleService;
    @Resource
    private RiskQueue riskQueue;

    @RestPack
    @GetMapping("paging")
    @SaCheckLogin
    @SaCheckPermission("risk:rule:list:view")
    public Page<RiskVoBoss> paging(Integer pageNumber, Integer pageSize, Long targetId, String name, Integer status) {
        return riskService.paging(pageNumber, pageSize, targetId, name, status).map(it -> {
            if (it.getTargetId() == 0L) {
                it.setTargetName("公共风控");
            } else {
                if (it.getDisposeObject().equals("card")) {
                    CardType cardType = cardTypeService.getByIdOpt(it.getTargetId()).orElseThrow(() -> new ProgramException("卡类型不存在"));
                    it.setTargetName(cardType.getSectionNo() + "(" + cardType.getChannelName() + ")");
                } else {
                    User user = userService.getByIdOpt(it.getTargetId()).orElseThrow(() -> new ProgramException("用户不存在"));
                    it.setTargetName(user.getAccount());
                }
            }
            return it;
        });
    }


    @RestPack
    @PostMapping("create")
    @SaCheckLogin
    @RepeatSubmit
    @SaCheckPermission("risk:rule:create")
    public Boolean create(@Validated @RequestBody CreateRiskDto createRiskDto) {
        if (createRiskDto.getRuleType()) {
            if (StringUtils.isEmpty(createRiskDto.getMccValue()) && StringUtils.isEmpty(createRiskDto.getCountryValue()) && StringUtils.isEmpty(createRiskDto.getMerchantValue())) {
                throw new ProgramException("至少填写一项风险值");
            }
        } else {
            if (StringUtils.isEmpty(createRiskDto.getFailValue()) && StringUtils.isEmpty(createRiskDto.getRefundValue()) && StringUtils.isEmpty(createRiskDto.getReversalValue()) && StringUtils.isEmpty(createRiskDto.getVerifyValue())) {
                throw new ProgramException("请填写完整风险值");
            }
            if (createRiskDto.getMinTransactionCount() == null || createRiskDto.getMinTransactionCount() < 1) {
                throw new ProgramException("最小交易次数必须大于等于1");
            }
        }
        for (Long targetId : createRiskDto.getIds()) {
            String message = "";
            if (targetId != 0L) {
                if (createRiskDto.getType().equals("card")) {
                    CardType cardType = cardTypeService.getByIdOpt(targetId).orElseThrow(() -> new ProgramException("卡类型不存在"));
                    message = cardType.getSectionNo();
                } else {
                    User user = userService.getByIdOpt(targetId).orElseThrow(() -> new ProgramException("用户不存在"));
                    message = user.getAccount();
                }
            }
            List<Long> riskIds = riskService.listByTargetId(targetId);
            List<RiskRule> riskRules = (riskIds == null || riskIds.isEmpty()) ? List.of() : riskRuleService.listByRiskId(riskIds);
            List<RiskRule> newRules = new ArrayList<>();
            if (createRiskDto.getRuleType()) {
                if (StringUtils.isNotBlank(createRiskDto.getMccValue())) {
                    if (riskRules.stream().anyMatch(
                            it -> it.getKey().equals("mcc") && it.getFun().equals(createRiskDto.getMccValue().substring(0, 2)) && it.getValue().equals(createRiskDto.getMccValue().substring(2)))) {
                        throw new ProgramException("所选" + message + "下MCC风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("mcc");
                        riskRule.setFun(createRiskDto.getMccValue().substring(0, 2));
                        riskRule.setValue(createRiskDto.getMccValue().substring(2));
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(createRiskDto.getCountryValue())) {
                    if (riskRules.stream().anyMatch(
                            it -> it.getKey().equals("country_code") && it.getFun().equals(createRiskDto.getCountryValue().substring(0, 2)) && it.getValue().equals(createRiskDto.getCountryValue().substring(2))
                    )) {
                        throw new ProgramException("所选" + message + "下国家风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("country_code");
                        riskRule.setFun(createRiskDto.getCountryValue().substring(0, 2));
                        riskRule.setValue(createRiskDto.getCountryValue().substring(2));
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(createRiskDto.getMerchantValue())) {
                    if (
                            riskRules.stream().anyMatch(
                                    it -> it.getKey().equals("merchant_name") && it.getFun().equals(createRiskDto.getMerchantValue().substring(0, 2)) && it.getValue().equals(createRiskDto.getMerchantValue().substring(2))
                            )
                    ) {
                        throw new ProgramException("所选" + message + "下商户风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("merchant_name");
                        riskRule.setFun(createRiskDto.getMerchantValue().substring(0, 2));
                        riskRule.setValue(createRiskDto.getMerchantValue().substring(2));
                        newRules.add(riskRule);
                    }
                }
            } else {
                if (StringUtils.isNotBlank(createRiskDto.getFailValue())) {
                    String[] split = createRiskDto.getFailValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("fail") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下失败次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("fail");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(createRiskDto.getRefundValue())) {
                    String[] split = createRiskDto.getRefundValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("refund") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下失败次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("refund");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(createRiskDto.getReversalValue())) {
                    String[] split = createRiskDto.getReversalValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("reversal") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下冲正次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("reversal");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(createRiskDto.getVerifyValue())) {
                    String[] split = createRiskDto.getVerifyValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("verify") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下验证次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("verify");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
            }
            Risk risk = new Risk();
            risk.setCode(createRiskDto.getCode());
            risk.setName(createRiskDto.getName());
            risk.setStatus(createRiskDto.getStatus());
            risk.setTargetId(targetId);
            risk.setDisposeObject(createRiskDto.getType());
            risk.setRuleType(createRiskDto.getRuleType());
            risk.setMinTransactionCount(createRiskDto.getMinTransactionCount());
            risk.setHasAuto(createRiskDto.getHasAuto());
            risk.setFunctionName(createRiskDto.getFunctionName());
            risk.setOperatorId(BossUserUtil.getId());
            riskService.save(risk);
            for (RiskRule riskRule : newRules) {
                riskRule.setRiskId(risk.getId());
            }
            riskRuleService.saveBatch(newRules);
        }
        riskQueue.refresh();
        return true;
    }


    @RestPack
    @PostMapping("updateStatus")
    @SaCheckLogin
    @RepeatSubmit
    @SaCheckPermission("risk:rule:edit")
    @SaCheckSafe("UPDATE_RISK")
    public Boolean updateStatus(@Validated @RequestBody UpdateRiskStatusDto updateRiskStatusDto) {
        Risk risk = riskService.getByIdOpt(updateRiskStatusDto.getId()).orElseThrow(() -> new ProgramException("风险不存在"));
        if (risk.getStatus().equals(updateRiskStatusDto.getStatus())) {
            throw new ProgramException("风险状态未发生变化");
        }
        Boolean status = riskService.updateStatus(updateRiskStatusDto.getId(), updateRiskStatusDto.getStatus());
        riskQueue.refresh();
        return status;

    }


    @RestPack
    @PostMapping("updateHasAuto")
    @SaCheckLogin
    @RepeatSubmit
    @SaCheckPermission("risk:rule:edit")
    @SaCheckSafe("UPDATE_RISK")
    public Boolean updateHasAuto(@Validated @RequestBody UpdateRiskHasAutoDto updateRiskHasAutoDto) {
        Risk risk = riskService.getByIdOpt(updateRiskHasAutoDto.getId()).orElseThrow(() -> new ProgramException("风险不存在"));
        if (risk.getHasAuto().equals(updateRiskHasAutoDto.getHasAuto())) {
            throw new ProgramException("风险状态未发生变化");
        }
        Boolean b = riskService.updateHasAutoRisk(updateRiskHasAutoDto.getId(), updateRiskHasAutoDto.getHasAuto());
        riskQueue.refresh();
        return b;
    }


    @RestPack
    @PostMapping("update")
    @SaCheckLogin
    @RepeatSubmit
    @SaCheckPermission("risk:rule:edit")
    @SaCheckSafe("UPDATE_RISK")
    public Boolean update(@Validated @RequestBody UpdateRiskDto updateRiskDto) {
        for (Long id : updateRiskDto.getIds()) {
            Risk risk = riskService.getByIdOpt(id).orElseThrow(() -> new ProgramException("风险不存在"));
            if (!risk.getCode().equals(updateRiskDto.getCode()) && riskService.existsByCode(updateRiskDto.getCode())) {
                throw new ProgramException("风险代码已存在");
            }
            Long targetId = risk.getTargetId();
            String message = "";
            if (targetId != 0L) {
                if (updateRiskDto.getType().equals("card")) {
                    CardType cardType = cardTypeService.getByIdOpt(targetId).orElseThrow(() -> new ProgramException("卡类型不存在"));
                    message = cardType.getSectionNo();
                } else {
                    User user = userService.getByIdOpt(targetId).orElseThrow(() -> new ProgramException("用户不存在"));
                    message = user.getAccount();
                }
            }
            List<Long> riskIds = riskService.listByTargetId(targetId);
            riskIds = riskIds.stream().filter(it -> !it.equals(risk.getId())).toList();
            List<RiskRule> riskRules = riskRuleService.listByRiskId(riskIds);
            List<RiskRule> newRules = new ArrayList<>();
            if (risk.getRuleType()) {
                if (StringUtils.isNotBlank(updateRiskDto.getMccValue())) {
                    if (riskRules.stream().anyMatch(
                            it -> it.getKey().equals("mcc") && it.getFun().equals(updateRiskDto.getMccValue().substring(0, 2)) && it.getValue().equals(updateRiskDto.getMccValue().substring(2)))) {
                        throw new ProgramException("所选" + message + "下MCC风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("mcc");
                        riskRule.setFun(updateRiskDto.getMccValue().substring(0, 2));
                        riskRule.setValue(updateRiskDto.getMccValue().substring(2));
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(updateRiskDto.getCountryValue())) {
                    if (riskRules.stream().anyMatch(
                            it -> it.getKey().equals("country_code") && it.getFun().equals(updateRiskDto.getCountryValue().substring(0, 2)) && it.getValue().equals(updateRiskDto.getCountryValue().substring(2))
                    )) {
                        throw new ProgramException("所选" + message + "下国家风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("country_code");
                        riskRule.setFun(updateRiskDto.getCountryValue().substring(0, 2));
                        riskRule.setValue(updateRiskDto.getCountryValue().substring(2));
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(updateRiskDto.getMerchantValue())) {
                    if (
                            riskRules.stream().anyMatch(
                                    it -> it.getKey().equals("merchant_name") && it.getFun().equals(updateRiskDto.getMerchantValue().substring(0, 2)) && it.getValue().equals(updateRiskDto.getMerchantValue().substring(2))
                            )
                    ) {
                        throw new ProgramException("所选" + message + "下商户风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("merchant_name");
                        riskRule.setFun(updateRiskDto.getMerchantValue().substring(0, 2));
                        riskRule.setValue(updateRiskDto.getMerchantValue().substring(2));
                        newRules.add(riskRule);
                    }
                }
            } else {
                if (StringUtils.isNotBlank(updateRiskDto.getFailValue())) {
                    String[] split = updateRiskDto.getFailValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("fail") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下失败次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("fail");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(updateRiskDto.getRefundValue())) {
                    String[] split = updateRiskDto.getRefundValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("refund") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下失败次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("refund");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(updateRiskDto.getReversalValue())) {
                    String[] split = updateRiskDto.getReversalValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("reversal") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下冲正次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("reversal");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
                if (StringUtils.isNotBlank(updateRiskDto.getVerifyValue())) {
                    String[] split = updateRiskDto.getVerifyValue().split("-");
                    if (
                            riskRules.stream().anyMatch(it -> it.getKey().equals("verify") && it.getFun().equals(split[0]) && it.getValue().equals(split[2]))
                    ) {
                        throw new ProgramException("所选" + message + "下验证次数风险值已存在");
                    } else {
                        RiskRule riskRule = new RiskRule();
                        riskRule.setKey("verify");
                        riskRule.setFun(split[0]);
                        riskRule.setDay(split[1]);
                        riskRule.setValue(split[2]);
                        newRules.add(riskRule);
                    }
                }
            }
            risk.setCode(updateRiskDto.getCode());
            risk.setName(updateRiskDto.getName());
            risk.setStatus(updateRiskDto.getStatus());
            risk.setTargetId(targetId);
            risk.setDisposeObject(updateRiskDto.getType());
            risk.setHasAuto(updateRiskDto.getHasAuto());
            risk.setFunctionName(updateRiskDto.getFunctionName());
            risk.setOperatorId(BossUserUtil.getId());
            risk.setDisposeObject(updateRiskDto.getType());
            risk.setMinTransactionCount(updateRiskDto.getMinTransactionCount());
            riskService.updateById(risk);
            riskRuleService.removeByRiskId(risk.getId());
            for (RiskRule riskRule : newRules) {
                riskRule.setRiskId(risk.getId());
            }
            riskRuleService.saveBatch(newRules);
        }
        riskQueue.refresh();
        return true;
    }


    @RestPack
    @PostMapping("remove")
    @SaCheckLogin
    @RepeatSubmit
    @SaCheckPermission("risk:rule:remove")
    @SaCheckSafe("REMOVE_RISK")
    public Boolean remove(@Validated @RequestBody DeleteRiskDto deleteRiskDto) {
        Risk risk = riskService.getByIdOpt(deleteRiskDto.getId()).orElseThrow(() -> new ProgramException("风险不存在"));
        if (risk.getStatus() != 1) {
            throw new ProgramException("只能删除禁用中的风险规则");
        }
        riskService.removeById(risk.getId());
        riskRuleService.removeByRiskId(risk.getId());
        riskQueue.refresh();
        return true;
    }


}
